{
    Array.prototype.shuffle = function() {
        for(
            var j, x, i = this.length; 
            i; 
            j = parseInt(Math.random() * i), 
            x = this[--i], 
            this[i] = this[j], 
            this[j] = x
        );
        return this;
    };

    Array.prototype.unique = function(){
        var res = [];
        var json = {};
        for(var i = 0; i < this.length; i++){
            if(!json[this[i]]){
                res.push(this[i]);
                json[this[i]] = 1;
            }
        }
        return res;
    }

    class Qtable {
        constructor (o = {
            actions: ["a", "b"],
            states: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        }) {

            this._o = o;
            this._table = [];
            this._selected = {};
            this.value = 0;
            this.size = {
                x: o.actions.length,
                y: o.states.length
            };

            for (let i = 0; i < o.actions.length; i++) {
                for (let j = 0; j < o.states.length; j++) {
                    this._table.push({
                        action: o.actions[i].toString(),
                        state: o.states[j].toString(),
                        value: 0
                    });
                }
            }
        }

        q(state, action) {
            let value = [];
            this._table.forEach(element => {
                if ( element.state === String(state) && element.action === String(action) ) {
                    this._selected = element;
                    value.push(element.value);
                }
                if ( element.state === String(state) && action === undefined ) { 
                    value.push(element.value);
                }
                if ( state === undefined && element.action === String(action) ) {
                    value.push(element.value);
                }
            });
            let val = value.length === 1 ? value[0] : value;
            this.value = val;
            return this;
        }

        set(value) {
            if ( this._selected ) {
                this._selected.value = value;
            }
            return this;
        }

        max(o) {
            let array = this._sort(o);
            let fristEle = array[0];
            let obj = {};
            if ( array.length ) {
                obj = {
                    name: o.action === "__require__" ? 
                        fristEle.action : fristEle.state,
                    value: fristEle.value
                }
            } else {
                obj = {
                    name: "left",
                    value: 0
                }
            }
            return obj;
        }

        min(o) {
            let array = this._sort(o);
            let lastEle = array[array.length - 1];
            let obj = {};
            if ( array.length ) {
                obj = {
                    name: o.action === "__require__" ? 
                            lastEle.action : lastEle.state,
                    value: lastEle.value
                }
            } else {
                obj = {
                    name: "left",
                    value: "0"
                }
            }
            return obj;
        }

        random(name) {
            let len = this._o[name].length;
            var idx = Math.floor(Math.random() * len + 1)-1;
            return this._o[name][idx];
        }

        in(type, value) {
            let isFind = false;
            if ( type === "state" ) {
                this._table.forEach(element => {
                    if ( element.state === value ) {
                        isFind = true;
                        //console.log(element);
                    }
                });
            } 
            if ( type === "action" ) {
                this._table.forEach(element => {
                    if ( element.action === value ) {
                        isFind = true;
                    }
                });
            }
            return isFind;
        }

        append(type, value) {
            if ( type === "state" ) {
                this._o.actions.forEach(element => {
                    this._table.push({
                        state: value,
                        action: element,
                        value: 0
                    });
                });
            }
            if ( type === "action" ) {
                this._o.states.forEach(element => {
                    this._table.push({
                        state: element,
                        action: value,
                        value: 0
                    });
                });
            }
        }

        getTable() {
            let table = [];

            const TableItem = (o, state) => {
                let obj = {state: state};
                o.actions.forEach((i) => {
                    obj[i] = "";
                });
                return obj;
            }

            let states = [];

            this._table.forEach(s => {
                states.push(s.state);
            });
            states = states.unique();
            states.forEach(s => {
                table.push(TableItem(this._o, String(s)));
            });
            table.forEach(newEle => {
                this._table.forEach(element => {
                    if ( newEle.state === element.state ) {
                        this._o.actions.forEach(action => {
                            if ( element.action === action ) {
                                newEle[action] = element.value;
                            }
                        });
                    }
                });
            });

            return table;
        }

        _sort(o) {
            let array = [];
            this._table.forEach(element => {
                if ( o.action === "__require__" ) {
                    if ( element.state === String(o.state) ) { 
                        array.push(element);
                    }
                } else {
                    if ( element.action === String(o.action) ) { 
                        array.push(element);
                    }
                }
            });
            array = array.shuffle();
            array.sort((p, n)=>{return n.value - p.value});
            return array;
        }
    }

    window.Qtable = Qtable;
}